﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace HRSystem.DAL
{
    public class MapDAL
    {
        public List<Point> GetMapFromTxt(string strFilePath, double shift)
        {
            //判断文件
            if (!File.Exists(strFilePath)) return null;
            //读取
            List<Point> points = new List<Point>();
            int i = 0;
            StreamReader sr = new StreamReader(strFilePath);
            //文件内容
            while (sr.Peek() > 0)
            {
                string line = sr.ReadLine();
                if (!string.IsNullOrEmpty(line))
                {
                    string[] ds = line.Split(' ');
                    foreach (string s in ds)
                    {
                        if (string.IsNullOrEmpty(s)) continue;
                        double angle = 45 + i * 360d / 5040d - shift;
                        i++;
                        if (s.Length > 7) continue;
                        int dis = int.Parse(s);
                        if (dis < 5000 && dis > 1000)
                            points.Add(new Point() { X = dis * Cos(angle), Y = dis * Sin(angle) });
                    }
                }
            }
            return points;
        }

        public class Parameter
        {
            /// <summary>
            /// 上边R2000的角度偏移
            /// </summary>
            public double UpAngleShift { get; set; }
            /// <summary>
            /// 下边R2000的角度偏移
            /// </summary>
            public double DownAngleShift { get; set; }
            /// <summary>
            /// 有效点与点之间距离阈值
            /// </summary>
            public double PointDistanceThrold { get; set; }
            /// <summary>
            /// 有效点簇的最小宽度
            /// </summary>
            public int PointsMinWidth { get; set; }
            /// <summary>
            /// 有效点簇的最小点数
            /// </summary>
            public int PointsMinCount { get; set; }
            /// <summary>
            /// X轴取值范围
            /// </summary>
            public int AreaX { get; set; }
            /// <summary>
            /// Y轴取值范围
            /// </summary>
            public int AreaY { get; set; }
            /// <summary>
            /// 盘体最小点数
            /// </summary>
            public int RimMinCount { get; set; }
            /// <summary>
            /// 盘壁最小点数
            /// </summary>
            public int LineMinCount { get; set; }
            /// <summary>
            /// 内壁特征夹角的容差阈值
            /// </summary>
            public int AngleThrold { get; set; }
            /// <summary>
            /// 下边R2000X轴偏移
            /// </summary>
            public int DownXShift { get; set; }
            /// <summary>
            /// 下边R2000Y轴偏移
            /// </summary>
            public int DownYShift { get; set; }
            /// <summary>
            /// 上边R2000X轴偏移
            /// </summary>
            public int UpXShift { get; set; }
            /// <summary>
            /// 上边R2000Y轴偏移
            /// </summary>
            public int UpYShift { get; set; }
            /// <summary>
            /// 进叉宽度
            /// </summary>
            public int Width { get; set; }
            /// <summary>
            /// 进叉高度
            /// </summary>
            public int Height { get; set; }
            /// <summary>
            /// 空间最大宽度
            /// </summary>
            public int MaxWidth { get; set; }

            public Parameter()
            {
            }
        }
        public class LineSegment
        {
            public Point BeginPoint { get; set; }
            public Point EndPoint { get; set; }
            public LineSegment(Point p1, Point p2)
            {
                BeginPoint = p1;
                EndPoint = p2;
            }
        }
        public void RemoveNoisePoint(List<Point> lstData, Parameter param, out List<Point> lstPoints)
        {
            lstPoints = new List<Point>();
            //分簇
            List<Point> lstTmpPoints = new List<Point>();//存储同组点临时集合
            bool isBegin = true;//标记是否是临时集合的开始点
            for (int i = lstData.Count - 1; i >= 0; i--)
            {
                if (isBegin)//是开始则添加当前点
                {
                    lstTmpPoints.Add(lstData[i]);
                    isBegin = false;
                }
                //下一个点
                int preIndex = i - 1;
                if (preIndex < 0)//循环结束
                {
                    if (GetDistanceOfTwoPoints(lstTmpPoints[0], lstTmpPoints[lstTmpPoints.Count - 1]) >= param.PointsMinWidth && 
                        lstTmpPoints.Count >= param.PointsMinCount)
                    {
                        lstPoints.AddRange(lstTmpPoints.ToList());
                    }
                    isBegin = true;
                    lstTmpPoints.Clear();//清空临时集合
                }
                else
                {
                    if (GetDistanceOfTwoPoints(lstData[i], lstData[preIndex]) < param.PointDistanceThrold)
                        lstTmpPoints.Add(lstData[preIndex]);
                    else
                    {
                        if (GetDistanceOfTwoPoints(lstTmpPoints[0], lstTmpPoints[lstTmpPoints.Count - 1]) >= param.PointsMinWidth && 
                            lstTmpPoints.Count >= param.PointsMinCount)
                        {
                            lstPoints.AddRange(lstTmpPoints.ToList());
                        }
                        isBegin = true;
                        lstTmpPoints.Clear();//清空临时集合
                    }
                }
            }
        }
        public void SelectPoints(List<Point> lstData, Parameter param, out List<Point> lstPoints)
        {
            lstPoints = new List<Point>();
            List<Point> lst = lstData.Where(p => p.X >= -param.AreaX && p.X <= param.AreaX).ToList();
            double miny = lst.Select(p => p.Y).Min();//Y轴一米内的最小Y
            var enums = lst.Where(p => p.Y <= miny + param.AreaY);
            double leftX = 0, rightX = 0;
            var enums_1 = enums.Where(p => p.X < 0 && p.Y >= miny + param.AreaY - 100 && p.Y <= miny + param.AreaY);
            var enums_2 = enums.Where(p => p.X > 0 && p.Y >= miny + param.AreaY - 100 && p.Y <= miny + param.AreaY);
            if (enums_1.Count() > 0)
                leftX = enums_1.OrderBy(p=>p.Y).Last().X;
            if (enums_2.Count() > 0)
                rightX = enums_2.OrderBy(p=>p.Y).Last().X;
            if (leftX == 0)
                leftX = rightX - param.AreaX;
            if (rightX == 0)
                rightX = leftX + param.AreaX;
            lstPoints = enums.Where(p => p.X >= leftX - param.AreaX / 2 && p.X <= rightX + param.AreaX / 2).ToList();
        }
        public bool IsCable(List<Point> lstData, Parameter param, out List<List<Point>> lstListPoints)
        {
            lstListPoints = new List<List<Point>>();
            List<List<Point>> lstListPoints_Tmp = new List<List<Point>>();
            //分簇
            List<Point> lstTmpPoints = new List<Point>();//存储同组点临时集合
            bool isBegin = true;//标记是否是临时集合的开始点
            for (int i = lstData.Count - 1; i >= 0; i--)
            {
                if (isBegin)//是开始则添加当前点
                {
                    lstTmpPoints.Add(lstData[i]);
                    isBegin = false;
                }
                //下一个点
                int preIndex = i - 1;
                if (preIndex < 0)//循环结束
                {
                    lstListPoints_Tmp.Add(lstTmpPoints.ToList());
                    isBegin = true;
                    lstTmpPoints.Clear();//清空临时集合
                }
                else
                {
                    if (GetDistanceOfTwoPoints(lstData[i], lstData[preIndex]) < param.PointDistanceThrold)
                        lstTmpPoints.Add(lstData[preIndex]);
                    else
                    {
                        lstListPoints_Tmp.Add(lstTmpPoints.ToList());
                        isBegin = true;
                        lstTmpPoints.Clear();//清空临时集合
                    }
                }
            }
            var enums = lstListPoints_Tmp.Where(p => p.Count > param.RimMinCount);//点数超出50的集合数须=2
            if (enums.Count() != 2)
                return false;
            foreach (var lst in enums)
            {
                List<double> lstDis = new List<double>();//内壁和侧面拐点，即距离收尾连线最远的点
                for (int i = 1; i < lst.Count - 1; i++)
                {
                    lstDis.Add(NodeToLineDistance(lst[i], lst.First(), lst.Last()));
                }
                int j = lstDis.IndexOf(lstDis.Max());
                var lst1 = lst.GetRange(0, j);//两段点集
                var lst2 = lst.GetRange(j, lst.Count - j);
                if (lst1.Count < param.LineMinCount || lst2.Count < param.LineMinCount)//两段数量限制
                    return false;
                double[] pars1 = MultiLine(lst1.Select(p => p.X).ToArray(), lst1.Select(p => p.Y).ToArray(), lst1.Count, 1);//拟合直线
                double ang1 = GetAngleByYX(Math.Abs(pars1[1]), pars1[1] / Math.Abs(pars1[1]));
                double[] pars2 = MultiLine(lst2.Select(p => p.X).ToArray(), lst2.Select(p => p.Y).ToArray(), lst2.Count, 1);//拟合直线
                double ang2 = GetAngleByYX(Math.Abs(pars2[1]), pars2[1] / Math.Abs(pars2[1]));
                if (Math.Abs(GetAngleBy(ang1, ang2)) - 90 > param.AngleThrold)//两条线夹角须接近90度
                    return false;
                double yoffset1 = Math.Abs(lst1.First().Y - lst1.Last().Y);//比较高度差
                double yoffset2 = Math.Abs(lst2.First().Y - lst2.Last().Y);
                lstListPoints.Add(yoffset1 > yoffset2 ? lst2 : lst1);
            }
            var enums_tmp = lstListPoints_Tmp.Where(p => p.Count <= param.RimMinCount);
            foreach (var lst in enums_tmp)
            {
                lstListPoints.Add(lst);
            }
            return true;
        }
        public List<LineSegment> GetLines(List<Point> lstData, Parameter param, int ud)
        {
            List<LineSegment> lines = new List<LineSegment>();
            lstData = lstData.OrderBy(p => p.X).ToList();
            double y = 0;
            if (ud == 1) y = param.UpYShift;
            else if (ud == 2) y = param.DownYShift;
            for (int i = 1; i < lstData.Count - 1; i++)
            {
                if (GetDistanceOfTwoPoints(lstData[i], lstData[i + 1]) > param.PointDistanceThrold)
                {
                    lines.Add(new LineSegment(new Point(lstData[i].X, y), new Point(lstData[i + 1].X, y)));
                    i++;
                }
            }
            return lines;
        }
        public bool GetArea(List<LineSegment> lines1, List<LineSegment> lines2, Parameter param, out int x, out int y)
        {
            x = 0; y = 0;
            List<Point> lst = new List<Point>();
            foreach (var lineUp in lines1)
            {
                foreach (var lineDown in lines2)
                {
                    double lengthUp = Math.Abs(lineUp.BeginPoint.X - lineUp.EndPoint.X);//上边长度
                    double lengthDown = Math.Abs(lineDown.BeginPoint.X - lineDown.EndPoint.X);//下边长度
                    double lengthLong = lengthUp > lengthDown ? lengthUp : lengthDown;
                    if (lengthLong < param.Width) continue;//长度太短直接跳过
                    if (lineUp.EndPoint.X < lineDown.BeginPoint.X || lineUp.BeginPoint.X > lineDown.EndPoint.X) continue;
                    if (lengthUp > param.MaxWidth || lengthDown > param.MaxWidth) continue;
                    double length1 = Math.Abs(lineUp.BeginPoint.X - lineDown.EndPoint.X);
                    double length2 = Math.Abs(lineUp.EndPoint.X - lineUp.BeginPoint.X);
                    double lthLong = length1 > length2 ? length1 : length2;
                    if (lthLong > param.MaxWidth) continue;
                    LineSegment lineLong = lineUp, lineShort = lineDown;//分别找出长边和短边
                    if (lengthDown > lengthUp) { lineLong = lineDown; lineShort = lineUp; }
                    double angleLeft = GetAngleByTwoPoint(lineLong.BeginPoint, lineShort.BeginPoint),//左右边线的角度
                        angleRight = GetAngleByTwoPoint(lineLong.EndPoint, lineShort.EndPoint);
                    if (angleRight > 0) angleRight = 180 - angleRight;
                    else angleRight += 180;
                    double wLeft = 0, wRight = 0;//取得左右边线在长边上的投影宽度
                    if (Math.Abs(angleLeft) < 90) wLeft = param.Height / Tan(Math.Abs(angleLeft));
                    if (Math.Abs(angleRight) < 90) wRight = param.Height / Tan(Math.Abs(angleRight));
                    if (wLeft + wRight + param.Width > lengthLong) continue;//宽度超出，不够进叉
                    //取得极限进叉位置，再取得最稳妥位置
                    double px = 0, py = 0;//进叉位置中心点
                    double ht = param.UpYShift - param.DownYShift;//上下R2000高度差
                    double zf = angleLeft / Math.Abs(angleLeft);//Y轴方向正负
                    if (Math.Abs(angleLeft) >= 90)//左侧钝角，只判断右侧
                    {
                        double h = (lengthLong - param.Width) * Tan(Math.Abs(angleRight));//极限高度
                        if (h > ht) h = ht;
                        px = lineLong.BeginPoint.X + (lengthLong - wRight) / 2;//开始点往右
                        py = lineLong.BeginPoint.Y + zf * (h / 2);//上下根据边线角度，区分正负
                    }
                    else if (Math.Abs(angleRight) >= 90)//右侧是钝角
                    {
                        double h = (lengthLong - param.Width) * Tan(Math.Abs(angleLeft));//极限高度
                        if (h > ht) h = ht;
                        px = lineLong.EndPoint.X - (lengthLong - wLeft) / 2;//结束点往左
                        py = lineLong.EndPoint.Y + zf * (h / 2);//上下根据边线角度，区分正负
                    }
                    else//都是锐角
                    {
                        //---h / Tan(Math.Abs(angleLeft)) + h / Tan(Math.Abs(angleRight)) = lengthLong - param.Width;
                        double h = (lengthLong - param.Width) * Tan(Math.Abs(angleLeft)) * Tan(Math.Abs(angleRight)) / (Tan(Math.Abs(angleLeft)) + Tan(Math.Abs(angleRight)));
                        if (h > ht) h = ht;
                        double wl = h / Tan(Math.Abs(angleLeft)), wr = h / Tan(Math.Abs(angleRight));
                        px = lineLong.BeginPoint.X + wl + (lengthLong - wl - wr) / 2;
                        py = lineLong.BeginPoint.Y + zf * (h / 2);//上下根据边线角度，区分正负
                    }
                    lst.Add(new Point(px, py));
                }
            }
            if (lst.Count == 0) return false;
            //结果集里挑出最近的进叉点
            if (lst.Count == 1) { x = (int)lst.First().X; y = (int)lst.First().Y; }
            else
            {
                List<double> lstDis = new List<double>();
                for (int i = 0; i < lst.Count; i++)
                {
                    lstDis.Add(Math.Abs(lst[i].X) + Math.Abs(lst[i].Y));
                }
                int id = lstDis.IndexOf(lstDis.Min());
                x = (int)lst[id].X; y = (int)lst[id].Y;
            }
            return true;
        }

        private double Sin(double angle)
        {
            return Math.Sin(angle * Math.PI / 180);
        }
        private double Cos(double angle)
        {
            return Math.Cos(angle * Math.PI / 180);
        }

        private double Tan(double angle)
        {
            return Math.Tan(angle * Math.PI / 180);
        }
        public Point GetPointOnMap(double originX, double originY, double zoomNum, double realX, double realY)
        {
            return new Point(originX + realX * zoomNum, originY - realY * zoomNum);
        }
        public double GetDistanceOfTwoPoints(Point p1, Point p2)
        {
            return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
        }
        private double NodeToLineDistance(double x, double y, double x1, double y1, double x2, double y2)
        {
            double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
            if (cross <= 0)
                return Math.Sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
            double d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
            if (cross > d2)
                return Math.Sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
            double r = cross / d2;
            double px = x1 + (x2 - x1) * r;
            double py = y1 + (y2 - y1) * r;
            return Math.Sqrt((x - px) * (x - px) + (py - y) * (py - y));
        }
        private double NodeToLineDistance(Point p, Point p1, Point p2)
        {
            return NodeToLineDistance(p.X, p.Y, p1.X, p1.Y, p2.X, p2.Y);
        }
        public double GetAngleByTwoPoint(Point p1, Point p2)
        {
            return GetAngleByYX(p2.Y - p1.Y, p2.X - p1.X);
        }
        public double GetAngleByYX(double y, double x)
        {
            return Math.Atan2(y, x) * 180 / Math.PI;
        }
        public double GetAngleBy(double jd_from, double jd_to)
        {
            double by = jd_to - jd_from;
            if (Math.Abs(by) > 180)
            {
                if (by > 0)
                    by = by - 360;
                else
                    by = 360 + by;
            }
            return by;
        }
        public List<Point> ConvertPoints(List<List<Point>> lstPoints)
        {
            List<Point> lst = new List<Point>();
            foreach (var lstPt in lstPoints)
            {
                lst.AddRange(lstPt);
            }
            return lst;
        }

        #region 曲线拟合
        ///<summary>
        ///用最小二乘法拟合二元多次曲线
        ///例如y=ax+b
        ///其中MultiLine将返回a，b两个参数。
        ///a对应MultiLine[1]
        ///b对应MultiLine[0]
        ///</summary>
        ///<param name="arrX">已知点的x坐标集合</param>
        ///<param name="arrY">已知点的y坐标集合</param>
        ///<param name="length">已知点的个数</param>
        ///<param name="dimension">方程的最高次数</param>
        public double[] MultiLine(double[] arrX, double[] arrY, int length, int dimension)//二元多次线性方程拟合曲线
        {
            int n = dimension + 1;                  //dimension次方程需要求 dimension+1个 系数
            double[,] Guass = new double[n, n + 1];      //高斯矩阵 例如：y=a0+a1*x+a2*x*x
            for (int i = 0; i < n; i++)
            {
                int j;
                for (j = 0; j < n; j++)
                {
                    Guass[i, j] = SumArr(arrX, j + i, length);
                }
                Guass[i, j] = SumArr(arrX, i, arrY, 1, length);
            }
            return ComputGauss(Guass, n);
        }
        private double SumArr(double[] arr, int n, int length) //求数组的元素的n次方的和
        {
            double s = 0;
            for (int i = 0; i < length; i++)
            {
                if (arr[i] != 0 || n != 0)
                    s = s + Math.Pow(arr[i], n);
                else
                    s = s + 1;
            }
            return s;
        }
        private double SumArr(double[] arr1, int n1, double[] arr2, int n2, int length)
        {
            double s = 0;
            for (int i = 0; i < length; i++)
            {
                if ((arr1[i] != 0 || n1 != 0) && (arr2[i] != 0 || n2 != 0))
                    s = s + Math.Pow(arr1[i], n1) * Math.Pow(arr2[i], n2);
                else
                    s = s + 1;
            }
            return s;
        }
        private double[] ComputGauss(double[,] Guass, int n)
        {
            int i, j;
            int k, m;
            double temp;
            double max;
            double s;
            double[] x = new double[n];
            for (i = 0; i < n; i++) x[i] = 0.0;//初始化
            for (j = 0; j < n; j++)
            {
                max = 0;
                k = j;
                for (i = j; i < n; i++)
                {
                    if (Math.Abs(Guass[i, j]) > max)
                    {
                        max = Guass[i, j];
                        k = i;
                    }
                }
                if (k != j)
                {
                    for (m = j; m < n + 1; m++)
                    {
                        temp = Guass[j, m];
                        Guass[j, m] = Guass[k, m];
                        Guass[k, m] = temp;
                    }
                }
                if (0 == max)
                {
                    // "此线性方程为奇异线性方程" 
                    return x;
                }
                for (i = j + 1; i < n; i++)
                {
                    s = Guass[i, j];
                    for (m = j; m < n + 1; m++)
                    {
                        Guass[i, m] = Guass[i, m] - Guass[j, m] * s / (Guass[j, j]);
                    }
                }
            }//结束for (j=0;j<n;j++)
            for (i = n - 1; i >= 0; i--)
            {
                s = 0;
                for (j = i + 1; j < n; j++)
                {
                    s = s + Guass[i, j] * x[j];
                }
                x[i] = (Guass[i, n] - s) / Guass[i, i];
            }
            return x;
        }//返回值是函数的系数
        #endregion
    }
}
